﻿#include "scheduler_impl.hh"
#include "../../src/repo/src/include/root/coroutine/coroutine.h"
#include "../util/time_util.hh"

kratos::service::SchedulerImpl::SchedulerImpl(ServiceBox *box) {}

bool kratos::service::ScheduleNode::operator<(const ScheduleNode &node) {
  return trigger_time > node.trigger_time;
}

kratos::service::SchedulerImpl::~SchedulerImpl() {
  for (const auto &it : timer_map_) {
    if (!it.second.expired()) {
      auto lock_handle = it.second.lock();
      // 关闭协程
      if (0 != lock_handle->co_id) {
        coro_close(lock_handle->co_id);
      }
    }
  }
  timer_map_.clear();
}

kratos::service::NodeShell kratos::service::SchedulerImpl::pop(std::time_t ms) {
  static NodeShell null;
  if (queue_.empty()) {
    return null;
  }
  // 取出到期的定时器
  if (queue_.top().node_ptr->trigger_time < ms) {
    auto handle = queue_.top();
    queue_.pop();
    return handle;
  }
  return null;
}

void kratos::service::SchedulerImpl::delete_from_map(std::uint64_t id) {
  auto it = timer_map_.find(id);
  if (it == timer_map_.end()) {
    return;
  }
  if (it->second.expired()) {
    // 按照现有的调用流程不应该发生
    timer_map_.erase(it);
    return;
  }
  auto lock_handle = it->second.lock();
  if (0 != lock_handle->co_id) {
    co_count_ -= 1;
  } else {
    noco_count_ -= 1;
  }
  timer_map_.erase(it);
}

auto kratos::service::SchedulerImpl::do_schedule(std::time_t ms,
                                                 std::size_t max_count)
    -> std::size_t {
  decltype(max_count) count = 0;
  if (!max_count) {
    max_count = 1;
  }
  auto milli_seconds = ms;
  if (!ms) {
    milli_seconds = util::get_os_time_millionsecond();
  }
  while (true) {
    if (count >= max_count) {
      // 达到最大限制
      break;
    }
    auto handle = pop(milli_seconds);
    if (!handle) {
      break;
    }
    if (!handle.node_ptr->deleted) {
      count += 1;
      // 调用定时器回调
      auto retval =
          handle.node_ptr->cb(handle.node_ptr->user_data, milli_seconds);
      if (!retval) {
        // 直接删除
        delete_from_map(handle.node_ptr->id);
      } else {
        if (handle.node_ptr->type == Type_::ONCE) {
          // 单次定时器
          delete_from_map(handle.node_ptr->id);
        } else if (handle.node_ptr->type == Type_::LOOP) {
          // 多次定时器
          // 调整时间放回队列
          handle.node_ptr->trigger_time =
              milli_seconds + handle.node_ptr->duration;
          queue_.emplace(std::move(handle));
        }
      }
    }
  }
  return count;
}

auto kratos::service::SchedulerImpl::schedule(std::time_t duration,
                                              SchedulerCallback cb,
                                              std::uint64_t udata)
    -> std::uint64_t {
  if (!duration || !cb) {
    return 0;
  }
  auto handle = std::make_shared<ScheduleNode>();
  handle->cb = cb;
  handle->type = Type_::ONCE;
  handle->duration = duration;
  handle->trigger_time = util::get_os_time_millionsecond() + duration;
  handle->user_data = udata;
  handle->id = timer_id_;
  timer_map_[handle->id] = handle;
  // 放入队列
  queue_.emplace(NodeShell(handle));
  noco_count_ += 1;
  return timer_id_++;
}

auto kratos::service::SchedulerImpl::schedule_co(std::time_t duration,
                                                 SchedulerCallback cb,
                                                 std::uint64_t udata)
    -> std::uint64_t {
  if (!duration || !cb) {
    return 0;
  }
  auto handle = std::make_shared<ScheduleNode>();
  handle->cb = cb;
  handle->type = Type_::ONCE;
  handle->duration = duration;
  handle->trigger_time = util::get_os_time_millionsecond() + duration;
  handle->user_data = udata;
  handle->id = timer_id_;
  handle->scheduler = this;
  // 启动协程，在协程内超时
  handle->co_id = coro_start([handle, duration](void *) {
    // 取得所有权，方式外部释放
    auto co_handle(handle);
    coro_sleep(duration);
    if (co_handle->deleted || !co_handle->scheduler) {
      // 销毁，外部已调整map
      return;
    }
    co_handle->cb(co_handle->user_data, util::get_os_time_millionsecond());
    // 单次，直接删除
    co_handle->scheduler->delete_from_map(co_handle->id);
  });
  timer_map_[handle->id] = handle;
  co_count_ += 1;
  return timer_id_++;
}

auto kratos::service::SchedulerImpl::schedule_many(std::time_t duration,
                                                   SchedulerCallback cb,
                                                   std::uint64_t udata)
    -> std::uint64_t {
  if (!duration || !cb) {
    return 0;
  }
  auto handle = std::make_shared<ScheduleNode>();
  handle->cb = cb;
  handle->type = Type_::LOOP;
  handle->duration = duration;
  handle->trigger_time = util::get_os_time_millionsecond() + duration;
  handle->user_data = udata;
  handle->id = timer_id_;
  timer_map_[handle->id] = handle;
  queue_.emplace(NodeShell(handle));
  noco_count_ += 1;
  return timer_id_++;
}

auto kratos::service::SchedulerImpl::schedule_co_many(std::time_t duration,
                                                      SchedulerCallback cb,
                                                      std::uint64_t udata)
    -> std::uint64_t {
  if (!duration || !cb) {
    return 0;
  }
  auto handle = std::make_shared<ScheduleNode>();
  handle->cb = cb;
  handle->type = Type_::ONCE;
  handle->duration = duration;
  handle->trigger_time = util::get_os_time_millionsecond() + duration;
  handle->user_data = udata;
  handle->id = timer_id_;
  handle->scheduler = this;
  handle->co_id = coro_start([handle, duration](void *) {
    // 取得所有权，方式外部释放
    auto co_handle(handle);
    auto sleep_duration = duration;
    while (true) {
      coro_sleep(sleep_duration);
      if (co_handle->deleted || !co_handle->scheduler) {
        // 销毁，外部已调整map
        return;
      }
      auto retval = co_handle->cb(co_handle->user_data,
                                  util::get_os_time_millionsecond());
      if (!retval) {
        co_handle->scheduler->delete_from_map(co_handle->id);
        // 直接销毁
        return;
      }
    }
  });
  co_count_ += 1;
  timer_map_[handle->id] = handle;
  return timer_id_++;
}

auto kratos::service::SchedulerImpl::cancel(std::uint64_t id) -> bool {
  auto it = timer_map_.find(id);
  if (it == timer_map_.end()) {
    // 没找到
    return false;
  }
  if (it->second.expired()) {
    // 不存在了，重复销毁
    timer_map_.erase(it);
    return false;
  }
  auto lock_handle = it->second.lock();
  if (0 != lock_handle->co_id) {
    // 关闭协程
    coro_close(lock_handle->co_id);
  } else {
    // 设置销毁标记
    lock_handle->deleted = true;
  }
  // 从表里销毁
  delete_from_map(id);
  return true;
}

auto kratos::service::SchedulerImpl::size() -> std::size_t {
  return timer_map_.size();
}

auto kratos::service::SchedulerImpl::size_co() -> std::size_t {
  return co_count_;
}

auto kratos::service::SchedulerImpl::size_noco() -> std::size_t {
  return noco_count_;
}

kratos::service::NodeShell::NodeShell(std::shared_ptr<ScheduleNode> ptr) {
  node_ptr = ptr;
}

kratos::service::NodeShell::NodeShell() {}

kratos::service::NodeShell::NodeShell(const NodeShell &rht) {
  node_ptr = rht.node_ptr;
}

kratos::service::NodeShell::NodeShell(NodeShell &&rht) noexcept {
  node_ptr = std::move(rht.node_ptr);
}

const kratos::service::NodeShell &
kratos::service::NodeShell::operator=(NodeShell &&rht) noexcept {
  node_ptr = std::move(rht.node_ptr);
  return *this;
}

bool kratos::service::NodeShell::operator<(
    const NodeShell &rht) const noexcept {
  return *node_ptr < *rht.node_ptr;
}

kratos::service::NodeShell::operator bool() noexcept { return (bool)node_ptr; }
